/*
  ivl_expand.c  -  implementation of point expanssion functions

  This file is part of the IVL Library (LIBIVL).
  
  LIBIVL - library for interval computations 
  
  Copyright (c) 2007 Mateusz Paprocki

  The IVL Library is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  The IVL Library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with the IVL Library; see the file LICENCE.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "interval.h"

int ivl_expand_u(ivl_ptr ivl, unsigned long x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_u(ivl, x);
	else {
		if (mpfr_cmp_ui(IVL_LEFT(ivl), x) > 0)
			inexact_left = mpfr_set_ui(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_cmp_ui(IVL_RIGHT(ivl), x) < 0)
			inexact_right = mpfr_set_ui(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
		
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_expand_s(ivl_ptr ivl, long x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_s(ivl, x);
	else {
		if (mpfr_cmp_si(IVL_LEFT(ivl), x) > 0)
			inexact_left = mpfr_set_si(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_cmp_si(IVL_RIGHT(ivl), x) < 0)
			inexact_right = mpfr_set_si(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
		
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;	
}

int ivl_expand_d(ivl_ptr ivl, double x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_d(ivl, x);
	else {
		if (mpfr_cmp_d(IVL_LEFT(ivl), x) > 0)
			inexact_left = mpfr_set_d(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_cmp_d(IVL_RIGHT(ivl), x) < 0)
			inexact_right = mpfr_set_d(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
	
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;	
}

int ivl_expand_z(ivl_ptr ivl, mpz_srcptr x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_z(ivl, x);
	else {
		if (mpfr_cmp_z(IVL_LEFT(ivl), x) > 0)
			inexact_left = mpfr_set_z(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_cmp_z(IVL_RIGHT(ivl), x) < 0)
			inexact_right = mpfr_set_z(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
	
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;	
}

int ivl_expand_q(ivl_ptr ivl, mpq_srcptr x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_q(ivl, x);
	else {
		if (mpfr_cmp_q(IVL_LEFT(ivl), x) > 0)
			inexact_left = mpfr_set_q(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_cmp_q(IVL_RIGHT(ivl), x) < 0)
			inexact_right = mpfr_set_q(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
	
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;
}

int ivl_expand_f(ivl_ptr ivl, mpfr_srcptr x) {
	int inexact = 0, inexact_left, inexact_right;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_f(ivl, x);
	else {
		if (mpfr_less_p(x, IVL_LEFT(ivl)))
			inexact_left = mpfr_set(IVL_LEFT(ivl), x, GMP_RNDD);
		else if (mpfr_greater_p(x, IVL_RIGHT(ivl)))
			inexact_right = mpfr_set(IVL_RIGHT(ivl), x, GMP_RNDU);
		else
			return 0;
	
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
	}
	
	return inexact;	
}

extern int ivl_expand_str(ivl_ptr ivl, const char *str, int base) {
	int inexact = 0, inexact_left, inexact_right;
	mpfr_t point;
	
	if (DOM_UNDEFINED(ivl))
		inexact = ivl_set_str(ivl, str, base);
	else {
		mpfr_init2(point, ivl_get_prec(ivl));
		
		inexact_left = mpfr_set_str(point, str, base, GMP_RNDD);
		
		if (inexact_left == -1) {
			mpfr_clear(point);
			return -1;
		}
			
		if (mpfr_less_p(point, IVL_LEFT(ivl)))
			mpfr_set(IVL_LEFT(ivl), point, GMP_RNDD);
		else {
			inexact_right = mpfr_set_str(point, str, base, GMP_RNDU);
		
			if (inexact_right == -1) {
				mpfr_clear(point);
				return -1;
			}
			
			if (mpfr_greater_p(point, IVL_RIGHT(ivl)))
				mpfr_set(IVL_LEFT(ivl), point, GMP_RNDU);
			else
				return 0;
		}
			
		IVL_CHECK(ivl, IVL_TOTAL, inexact, inexact_left, inexact_right);
		
		mpfr_clear(point);
	}
	
	return inexact;
}

void ivl_expand_inf(ivl_ptr ivl, long sign) {
	if (DOM_UNDEFINED(ivl))
		ivl_set_inf(ivl, sign);
	else {
		switch (sign) {
			case SGN_NEGATIVE:
				mpfr_set_inf(IVL_LEFT(ivl), sign);
				break;
			case SGN_POSITIVE:
				mpfr_set_inf(IVL_RIGHT(ivl), sign);
				break;
			case SGN_ZERO:
				ivl->domain = IVL_UNDEFINED;
				break;
		}
	}
}
